using System;
using System.Collections;
using DG.Tweening;
using Minesweeper.GUI.HUD;
using UnityEngine;
using UnityEngine.Tilemaps;
using static Minesweeper.GameStatic;

namespace Minesweeper
{
    [RequireComponent(typeof(SpriteRenderer))]
    public class Player : MonoBehaviour
    {
        public static Player Instance;
    
        public Vector2Int position;
    
        public Sprite right, up, down;

        public new Camera camera;
        public float scrollSensitivity = 1;
        public float minSize = 1, maxSize = 10;
        public float cursorFollowSensitivity = 0.25f;
        public float followSpeed = 4;

        public float maxHealth = 100;
        public float maxShovelDurability = 32;
        public Transform shovel;
    
        [NonSerialized] public float Health;
        [NonSerialized] public float ShovelDurability;
    
        private SpriteRenderer SpriteRenderer => GetComponent<SpriteRenderer>();

        private float _curSize;
    
        private void Awake()
        {
            Instance = this;
            Health = maxHealth;
            ShovelDurability = maxShovelDurability;
            _curSize = (minSize + maxSize) / 2;
            camera.orthographicSize = _curSize;
        }

        [NonSerialized] public bool Digging;
        private float _diggingStartTime;
    
        public void StartDigging(Vector2Int pos)
        {
            _shovelDirection = Mathf.Atan2(pos.y - position.y, pos.x - position.x) * Mathf.Rad2Deg;
            shovel.gameObject.SetActive(true);
            _diggingStartTime = Time.time;
            Digging = true;
        }
    
        public void StopDigging()
        {
            shovel.gameObject.SetActive(false);
            ShovelDurability--;
            Digging = false;
        }

        private IEnumerator ProcessMove(KeyCode k, Action a)
        {
            while (true)
            {
                if (Paused || Digging || !Input.GetKey(k))
                    break;
                a();
                yield return new WaitForSeconds(.15f);
            }
        }

        private Coroutine _up, _down, _left, _right;
        private float _shovelDirection;

        private void Update()
        {
            if (Paused)
                return;

            if (!Digging)
            {
                if (Input.GetKeyDown(KeyCode.W))
                {
                    if (_up != null)
                        StopCoroutine(_up);
                    _up = StartCoroutine(ProcessMove(KeyCode.W, MoveUp));
                }
                if (Input.GetKeyDown(KeyCode.S))
                {
                    if (_down != null)
                        StopCoroutine(_down);
                    _down = StartCoroutine(ProcessMove(KeyCode.S, MoveDown));
                }
                if (Input.GetKeyDown(KeyCode.A))
                {
                    if (_left != null)
                        StopCoroutine(_left);
                    _left = StartCoroutine(ProcessMove(KeyCode.A, MoveLeft));
                }
                if (Input.GetKeyDown(KeyCode.D))
                {
                    if (_right != null)
                        StopCoroutine(_right);
                    _right = StartCoroutine(ProcessMove(KeyCode.D, MoveRight));
                }
            }
        
            var scrollDelta = Input.mouseScrollDelta.y;
            if (scrollDelta != 0)
            {
                _curSize -= scrollDelta * scrollSensitivity;
                if (_curSize < minSize)
                    _curSize = minSize;
                if (_curSize > maxSize)
                    _curSize = maxSize;
                camera.DOOrthoSize(_curSize, 0.5f).SetEase(Ease.OutSine);
            }

            Vector2 p = Input.mousePosition;
            var sr = camera.pixelWidth / camera.pixelHeight;
            var hr = 2 * p.x / camera.pixelWidth - 1;
            var vr = 2 * p.y / camera.pixelHeight - 1;
            var ov = (Vector2)transform.position + new Vector2(hr * cursorFollowSensitivity * camera.orthographicSize * sr, 
                vr * cursorFollowSensitivity * camera.orthographicSize);
        
            if ((Vector2)camera.transform.position != ov)
            {
                var camPos = Vector2.Lerp(camera.transform.position, ov, followSpeed * Time.deltaTime);
                camera.transform.position = new Vector3(camPos.x, camPos.y, -10);
            }
            else
            {
                camera.transform.position = new Vector3(ov.x, ov.y, -10);
            }

            if (Digging)
            {
                var time = Time.time - _diggingStartTime;
                var sin = Mathf.Sin(time * Mathf.PI * 2) * 30;
                shovel.position = transform.position;
                shovel.eulerAngles = new Vector3(0, 0, sin + _shovelDirection);
            }
        }
    
        private static GameGenerator Gg => GameGenerator.Instance;

        public void Damage(int dmg)
        {
            Health -= dmg;
            if (Health <= 0)
            {
                // Do something which kills player.
            }
        }

        private static bool CanMoveTo(TileBase t) => t == Gg.tileUndiscovered || t == Gg.tileMark || t == Gg.tileExploded;

        private void MoveUp()
        {
            SpriteRenderer.sprite = up;
            var t = Gg.Tilemap.GetTile((Vector3Int)position + Vector3Int.up);
            if (CanMoveTo(t))
                return;
            position += Vector2Int.up;
            transform.DOMove((Vector2)position, .15f).SetEase(Ease.OutSine);
            Minimap.Instance.Move(position);
        }

        private void MoveDown()
        {
            SpriteRenderer.sprite = down;
            var t = Gg.Tilemap.GetTile((Vector3Int)position + Vector3Int.down);
            if (CanMoveTo(t))
                return;
            position += Vector2Int.down;
            transform.DOMove((Vector2)position, .15f).SetEase(Ease.OutSine);
            Minimap.Instance.Move(position);
        }
    
        private void MoveLeft()
        {
            SpriteRenderer.sprite = right;
            transform.DOScaleX(-1, .15f).SetEase(Ease.OutSine);
            var t = Gg.Tilemap.GetTile((Vector3Int)position + Vector3Int.left);
            if (CanMoveTo(t))
                return;
            position += Vector2Int.left;
            transform.DOMove((Vector2)position, .15f).SetEase(Ease.OutSine);
            Minimap.Instance.Move(position);
        }

        private void MoveRight()
        {
            SpriteRenderer.sprite = right;
            transform.DOScaleX(1, .15f).SetEase(Ease.OutSine);
            var t = Gg.Tilemap.GetTile((Vector3Int)position + Vector3Int.right);
            if (CanMoveTo(t))
                return;
            position += Vector2Int.right;
            transform.DOMove((Vector2)position, .15f).SetEase(Ease.OutSine);  
            Minimap.Instance.Move(position);
        }
    }
}
